Vue.js doesn’t come with any form validation capabilities by default.
Therefore, we need to add our own form validation library or with our own code.
In this article, we’ll look at how to validate forms with Vuelidate.
Asynchronous Validation
We can validate form fields asynchronously.
For example, we can write:
<template>
<div id="app">
<div :class="{ 'form-group--error': $v.$error }">
<label>Name</label>
<input v-model.trim="$v.name.$model">
</div>
<div v-if="!$v.name.required">Name is required.</div>
<div v-if="!$v.name.isUnique">Name already exists.</div>
</div>
</template>
<script>
import { required } from "vuelidate/lib/validators";
export default {
name: "App",
data() {
return {
name: ""
};
},
validations: {
name: {
required,
isUnique(value) {
if (value === "") return true;
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(typeof value === "string" && value.length % 2 !== 0);
}, 350 + Math.random() * 300);
});
}
}
}
};
</script>
We have the isUnique
method that returns a promise that resolves to true
if value
is string and its length is even.
value
is the value that we inputted.
true
means the value
is invalid.
In the template, we just check the rule with the $v.name.isUnique
property.
We can also use the async
and await
syntax for async validation.
We just return the resolved value inside the function:
validations: {
async isUnique (value) {
if (value === '') return true
const response = await fetch(`/api/unique/${value}`)
return Boolean(await response.json())
}
}
Delayed Validation Errors
We can delay validation errors.
To do that, we can create a touch
method that is delayed.
For example, we can write:
<template>
<div id="app">
<div :class="{ 'form-group--error': $v.$error }">
<label>Name</label>
<input v-model.trim="$v.name.$model" @input="delayTouch($v.name)">
</div>
<div v-if="!$v.name.required">Name is required.</div>
<div
class="error"
v-if="!$v.name.minLength"
>Name must have at least {{$v.name.$params.minLength.min}} letters.</div>
<div
class="error"
v-if="!$v.name.maxLength"
>Name must have at most {{$v.name.$params.maxLength.max}} letters.</div>
</div>
</template>
<script>
import { required, minLength, maxLength } from "vuelidate/lib/validators";
const touchMap = new WeakMap();
export default {
name: "App",
data() {
return {
name: ""
};
},
validations: {
name: {
required,
minLength: minLength(4),
maxLength: maxLength(15)
}
},
methods: {
delayTouch($v) {
$v.$reset();
if (touchMap.has($v)) {
clearTimeout(touchMap.get($v));
}
touchMap.set($v, setTimeout($v.$touch, 1000));
}
}
};
</script>
to add a delayTouch
method to run the form validation code with a delay.
We have a weak map to check store the timer that runs $v.$touch
with a delay.
If the timer exists in the weak map, we clear the timeout.
This way, we won’t have to create a new timer every time delayTouch
is run.
delayTouch
is run when the input event is emitted.
Accessing Validator Parameters
We can access the $params
property of the child field.
For example, we can write:
<template>
<div id="app">
<div :class="{ 'form-group--error': $v.$error }">
<label>Name</label>
<input v-model.trim="$v.form.name.$model">
</div>
<div v-if="!$v.form.name.required">Name is required.</div>
<div
v-if="!$v.form.name.minLength"
>Name must have at least {{$v.form.name.$params.minLength.min}} letters.</div>
<div
v-if="!$v.form.name.maxLength"
>Name must have at most {{$v.form.name.$params.maxLength.max}} letters.</div>
</div>
</template>
<script>
import { required, minLength, maxLength } from "vuelidate/lib/validators";
export default {
name: "App",
data() {
return {
form: {
name: ""
}
};
},
validations: {
form: {
name: {
required,
minLength: minLength(4),
maxLength: maxLength(15)
}
}
}
};
</script>
We have the $v.form.name.$params.minLength.min
and $v.form.name.$params.maxLength.max
properties to access the min and max number of allowed characters for the name
field.
Conclusion
We can validate form fields asynchronously and access form validation rules data with Vuelidate.